package cn.qingfeng.demo.transaction.springboot.programmable.service.impl;

import cn.qingfeng.demo.transaction.springboot.common.entity.User;
import cn.qingfeng.demo.transaction.springboot.common.mapper.UserMapper;
import cn.qingfeng.demo.transaction.springboot.programmable.service.TransactionalDemo2Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.UUID;

/**
 * springboot编程式事务管理与声明式事务管理事务传播规则类似。
 * 不同点是：编程式事务管理在private修饰和内部调用（一个类中的方法相互调用）时并不会使事务失效。
 * <p>
 * spring有事务传播机制，所以不需要像jdbc那样调用savePoint或commit或rollback方法。
 * 如果整个方法都包裹在transactionTemplate.execute中，就等效于使用@Transactional注解。
 * <p>
 * 声明式事务管理可以通过拆分来实现与编程式事务管理相同的效果。只是事务力度过细的话声明式事务管理需要拆分成很多类，比较麻烦。
 * <p>
 * 根据粒度大小合理选择以下方式：切面编程式事务管理》声明式事务管理》编程式事务管理
 *
 * 因为springboot编程式事务管理写在lambda表达式中，所以不支持必检异常的回滚。
 */
@Service
public class TransactionalDemo2ServiceImpl implements TransactionalDemo2Service {
    @Autowired
    private UserMapper userMapper;

    @Autowired
    private TransactionTemplate transactionTemplate;

    private void addUser0() {
        //调用execute，会在方法正常执行完毕后总的commit一次。
        transactionTemplate.execute(status -> {
            User user = new User("user1", UUID.randomUUID().toString());
            userMapper.add(user);
            System.out.println("user1向数据库中插入了User----" + user);

            return new Object();
        });
    }

    private void addUser00() {
        //调用execute，会在方法正常执行完毕后总的commit一次。
        transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
//        transactionTemplate.setPropagationBehaviorName("PROPAGATION_REQUIRES_NEW");
        transactionTemplate.execute(status -> {
            User user = new User("user1", UUID.randomUUID().toString());
            userMapper.add(user);
            System.out.println("user1向数据库中插入了User----" + user);

            return new Object();
        });
    }

    @Override
    public void addUser000() {
        //调用execute，会在方法正常执行完毕后总的commit一次。
        transactionTemplate.execute(status -> {
            User user = new User("user1", UUID.randomUUID().toString());
            userMapper.add(user);//回滚
            System.out.println("user000向数据库中插入了User----" + user);
            throw new RuntimeException();
        });
    }

    @Override
    public void addUser() {
        //调用execute，会在方法正常执行完毕后总的commit一次。
        transactionTemplate.execute(status -> {
            User user = new User("user1", UUID.randomUUID().toString());
            userMapper.add(user);//回滚
            System.out.println("user1向数据库中插入了User----" + user);

            System.out.println(1 / 0);

            //以下不执行
            User user2 = new User("user2", UUID.randomUUID().toString());
            userMapper.add(user2);
            System.out.println("user2向数据库中插入了User----" + user2);

            return new Object();
        });
    }

    @Override
    public void addUser2() {
        transactionTemplate.execute(status -> {
            User user = new User("user1", UUID.randomUUID().toString());
            userMapper.add(user);//回滚
            System.out.println("user1向数据库中插入了User----" + user);

            User user2 = new User("user2", UUID.randomUUID().toString());
            userMapper.add(user2);//回滚
            System.out.println("user2向数据库中插入了User----" + user2);

            System.out.println(1 / 0);

            return new Object();
        });
    }

    @Override
    public void addUser3() {
        transactionTemplate.execute(status -> {
            User user3 = new User("user3", UUID.randomUUID().toString());
            userMapper.add(user3);//回滚
            System.out.println("user3向数据库中插入了User----" + user3);

            addUser2();//回滚

            return true;
        });
    }

    @Override
    public void addUser4() {
        transactionTemplate.execute(status -> {
            User user3 = new User("user3", UUID.randomUUID().toString());
            userMapper.add(user3);//回滚
            System.out.println("user3向数据库中插入了User----" + user3);

            addUser0();//回滚

            System.out.println(1 / 0);

            return true;
        });
    }

    @Override
    public void addUser5() {
        transactionTemplate.execute(status -> {
            System.out.println(status);
            System.out.println(Propagation.NESTED.toString());

            User user3 = new User("user3", UUID.randomUUID().toString());
            userMapper.add(user3);//回滚
            System.out.println("user3向数据库中插入了User----" + user3);

            addUser00();//落表成功

            System.out.println(1 / 0);

            return true;
        });
    }
}
